New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Replays - Install missing map #13233
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The overall approach here looks good, and naturally extends towards our wishlist goal of showing players and spawns in the server list map previews. I haven't looked at the replay part of this in any detail yet, but here's some issues with the map preview logic to start with.
Please also split this into separate commits for refactoring (changing LobbyMapPreviewLogic to work from additional places) vs new features (making the ReplayBrowserLogic use it). This makes it much easier to understand changes when looking back through the git history.
m => WidgetUtils.TruncateText("Created by {0}".F(lobby.Map.Author), authorLabel.Bounds.Width, font)); | ||
authorLabel.GetText = () => author.Update(lobby.Map); | ||
} | ||
available.IsVisible = () => getMap().Status == MapStatus.Available && (!getMap().RulesLoaded || !getMap().InvalidCustomRules); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our map handling code and parts of our UI are multithreaded, so seeing lines like this makes me uneasy.
We knew from the design of LobbyLogic
that lobby.Map
would not change between evaluating e.g. lobby.Map.Status
andlobby.Map.RulesLoaded
, but we don't have that guarantee for arbitrary getMap
s.
Please change these to query getMap()
once per evaluation, e.g. here:
available.IsVisible = () =>
{
var map = getMap();
return map.Status == MapStatus.Available && (!map.RulesLoaded || !map.InvalidCustomRules);
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The natural question that follows this is whether we can cache getMap
across the different widget bindings. The answer is probably, but it is probably not worth it. You could in principle override the ChromeLogic.Tick
and then set a field in the class that is used in all the bindings, but I expect that this would introduce a noticable latency, and possibly other side effects.
So long as the map usage is consistent within individual bindings, it's generally not a big deal if it changes between evaluating different widgets. The worst case scenario is that e.g. the title and author labels are out of sync for 1/60th of a second until the next frame is rendered.
|
||
var install = download.GetOrNull<ButtonWidget>("MAP_INSTALL"); | ||
if (install != null) | ||
{ | ||
install.OnClick = () => lobby.Map.Install(mapRepository, () => | ||
install.OnClick = () => getMap().Install(mapRepository, () => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a good example of the getMap()
danger: it generally takes between half and a few seconds to install a map, and its quite possible for the host to change map between getMap().Install
and getMap().PreloadRules
running.
The fix again is to evaluate it once to be certain that it can't change under you:
install.OnClick = () =>
{
var map = getMap();
map.Install(mapRepository, () =>
{
map.PreloadRules();
if (orderManager != null)
Game.RunAfterTick(() => orderManager.IssueOrder(Order.Command("state {0}".F(Session.ClientState.NotReady))));
}
};
@@ -22,100 +24,39 @@ public class LobbyMapPreviewLogic : ChromeLogic | |||
int blinkTick; | |||
|
|||
[ObjectCreator.UseCtor] | |||
internal LobbyMapPreviewLogic(Widget widget, ModData modData, OrderManager orderManager, LobbyLogic lobby) | |||
internal LobbyMapPreviewLogic(Widget widget, ModData modData, OrderManager orderManager, Func<MapPreview> getMap, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is no longer Lobby specific, so the class and file should be renamed.
@@ -27,24 +27,28 @@ public class ReplayBrowserLogic : ChromeLogic | |||
{ | |||
static Filter filter = new Filter(); | |||
|
|||
public MapPreview Map { get; private set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't need to be public?
|
||
if (replay == null) | ||
return; | ||
|
||
try | ||
{ | ||
if (Map.Status != MapStatus.Available) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style nit: don't leave hanging blocks. The hanging else-if is ambiguous between the inner if and the outer if (with an indentation error), so this needs braces to make it explicit.
Updated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor nits, but I like it.
|
||
MapPreview Map { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this a property at all, if you're making both accessors private?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LobbyLogic.Map was used in LobbyUtils so public get. I pass now Map instead of LobbyLogic so public was removed. I'll change it to field.
|
||
Dictionary<CPos, SpawnOccupant> selectedSpawns; | ||
MapPreview Map { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto, may as well just be a field here.
Updated - property Map replaced by field. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, thanks for taking this feature on. 👍 after a few small tweaks:
var map = getMap(); | ||
return map.Status == MapStatus.Available && (!map.RulesLoaded || !map.InvalidCustomRules); | ||
}; | ||
SetupWidgets(available, getMap, onMouseDown, getSpawnOccupants); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style nit: newline between closing braces and the next statement.
var map = getMap(); | ||
return map.Status == MapStatus.Available && map.InvalidCustomRules; | ||
}; | ||
SetupWidgets(invalid, getMap, onMouseDown, getSpawnOccupants); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here too.
var map = getMap(); | ||
return map.Status != MapStatus.Available && map.Status != MapStatus.DownloadAvailable; | ||
}; | ||
SetupWidgets(progress, getMap, onMouseDown, getSpawnOccupants); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here too.
statusSearching.IsVisible = () => getMap().Status == MapStatus.Searching; | ||
|
||
var statusUnavailable = progress.GetOrNull("MAP_STATUS_UNAVAILABLE"); | ||
if (statusUnavailable != null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style nit: put braces around hanging blocks.
{ "getMap", (Func<MapPreview>)(() => Map) }, | ||
{ "onMouseDown", (Action<MapPreviewWidget, MapPreview, MouseInput>)((preview, map, mi) => { }) }, | ||
{ "getSpawnOccupants", (Func<MapPreview, Dictionary<CPos, SpawnOccupant>>)(map => LobbyUtils.GetSpawnOccupants(selectedReplay.GameInfo.Players, map)) }, | ||
}); | ||
|
||
panel.Get<LabelWidget>("DURATION").GetText = () => WidgetUtils.FormatTimeSeconds((int)selectedReplay.GameInfo.Duration.TotalSeconds); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please change this to the following while you're here?
var replayDuration = new CachedTransform<ReplayMetadata, string>(r =>
"Duration: {0}".F(WidgetUtils.FormatTimeSeconds((int)selectedReplay.GameInfo.Duration.TotalSeconds)));
panel.Get<LabelWidget>("DURATION").GetText = () => replayDuration.Update(selectedReplay);
This makes the label look less disconnected when the install map button is showing, plus a small perf win.
selectedSpawns = (selectedReplay != null) | ||
? LobbyUtils.GetSpawnOccupants(selectedReplay.GameInfo.Players, selectedReplay.GameInfo.MapPreview) | ||
: new Dictionary<CPos, SpawnOccupant>(); | ||
map = (selectedReplay != null) ? selectedReplay.GameInfo.MapPreview : MapCache.UnknownMap; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style nit: unnecessary parenthesis around selectedReplay != null
.
Updated. |
Can anyone agree that the PR is getting compiled .... i get errors like "local variable map is already define and gets overwritten" |
Same here, the last commit is to blame. Needs to be reverted or fixed. |
The PR before merge was built succesfully (also locally). There is simple fix to avoid this 'warning as error'. Could you please review quickly #13071? It by chance removes the local 'map' argument so solves this issue. |
#13071 reduces the errors, but lines 387 and 401 in LobbyLogic still have that issue there. |
This should be fixed as its own PR, separate to adding any new features. |
#13071 Updated - map parameter renamed, compiled now with the same MSBuild, so should be fine now. |
Fixed by #13313. |
Replay's Map preview unification with Lobby map preview - Install map, map author, map title tooltip, ...
Closes #11149.